You need to add some bread to your game. The bread will be the bat that
the player uses to hit the cheese around the screen. You think that
tomatoes might make good targets, but first you need to get the bread
working.
You need to store all the
same information about the bread as you do about the cheese. It has a
position, a texture, and a speed. The only difference is in the Update
behavior. Whereas the cheese travels in a particular direction each
time it’s updated and bounces off the edges of the playing field, the
bread is controlled by one of the thumbsticks on gamepad 1. In the game,
you need to store the same information for the cheese and bread, so you
could go ahead and create all the class member variables for them as
follows:
Texture2D cheeseTexture;
Rectangle cheeseRectangle;
float cheeseX;
float cheeseXSpeed;
float cheeseY;
float cheeseYSpeed;
float cheeseWidthFactor = 0.05f;
float cheeseTicksToCrossScreen = 200.0f;
Texture2D breadTexture;
Rectangle breadRectangle;
float breadX;
float breadXSpeed;
float breadY;
float breadYSpeed;
float breadWidthFactor = 0.05f;
float breadTicksToCrossScreen = 200.0f;
This
code simply has a copy of all the cheese variables, but renamed for
bread. However, from a programming point of view, this is not really the
best way to do it. The Great Programmer would certainly not approve.
She doesn’t like it when you have lots of separate variables all
relating to one thing. She reckons that all the information about a
particular item should be grouped together in one place. There should be
a "cheese group" and a "bread group."
You’ve seen this
"grouping together" in Microsoft XNA ever since you started writing
programs. For example, you know that XNA holds Color
information in the form of a structure with fields that represent the
red, green, and blue intensities of a particular color. For your bread
and cheese, you’d like to group all this information together in the
same way.
1. Using a Structure to Hold Sprite Information
C# provides a kind of object called a structure
to allow programmers to group things together. Structures are like
classes, in that they can contain methods and data, but they are managed
by value.
The fact that structures are managed by value makes them ideal for
holding small lumps of data that we want to treat as a whole. You can
design a structure that holds all the information about a sprite on the
screen as follows:
struct GameSpriteStruct
{
public Texture2D SpriteTexture;
public Rectangle SpriteRectangle;
public float X;
public float Y;
public float XSpeed;
public float YSpeed;
public float WidthFactor;
public float TicksToCrossScreen;
}
Each of the items in the structure is a field. If you compare the fields of the structure GameSpriteStruct
with the variables you used in the original bouncing cheese program,
you find that it holds all the information you need for a sprite: the
texture, the rectangle in which to draw the sprite, the current position
of the sprite, the speed at which the sprite moves, and the size and
speed settings. Once you’ve created this structure, you can declare
variables of this type for use in your game:
GameSpriteStruct cheese;
GameSpriteStruct bread;
When you declare a GameSpriteStruct
variable, you get a structure that contains all the fields grouped
together in it. You can then use the fields in the structure as follows:
cheese.SpriteTexture = Content.Load<Texture2D>("Images/Cheese");
bread.SpriteTexture = Content.Load<Texture2D>("Images/Bread");
These statements set
the textures for the bread and cheese to ones loaded from images placed
in your project content. You can get hold of any of the fields in your
structure by following the name of the structure variable with a period
(.) and then the name of the field. This works because you’ve made the
fields public. If you look back to the declaration of GameSpriteStruct, you see that each field has the C# keyword public in front of it. Words placed in front of fields like this are called modifiers. There are a number of different modifiers in C#; public is an "access modifier," in that it determines the level of access to a field. Fields marked as public can be used by code outside the class or structure. You can make fields private so that code in methods outside the class or structure can’t read or write the value in the field. For now, though, public
fields are fine because they are easy to use and you don’t have any
particular need for security. Now that you have your bread and cheese
structures, you can set the values in them:
void scaleSprites()
{
cheese.TicksToCrossScreen = 200.0f;
cheese.WidthFactor = 0.05f;
cheese.SpriteRectangle.Width =
(int)((displayWidth * cheese.WidthFactor) + 0.5f);
float aspectRatio =
(float)cheese.SpriteTexture.Width / cheese.SpriteTexture.Height;
cheese.SpriteRectangle.Height =
(int)((cheese.SpriteRectangle.Width / aspectRatio) + 0.5f);
cheese.X = minDisplayX;
cheese.Y = minDisplayY;
cheese.XSpeed = displayWidth / cheese.TicksToCrossScreen;
cheese.YSpeed = cheese.XSpeed;
bread.WidthFactor = 0.15f;
bread.TicksToCrossScreen = 120.0f;
bread.SpriteRectangle.Width =
(int)((displayWidth * bread.WidthFactor) + 0.5f);
aspectRatio =
(float)bread.SpriteTexture.Width / bread.SpriteTexture.Height;
bread.SpriteRectangle.Height =
(int)((bread.SpriteRectangle.Width / aspectRatio) + 0.5f);
bread.X = displayWidth / 2;
bread.Y = displayHeight / 2;
bread.XSpeed = displayWidth / bread.TicksToCrossScreen;
bread.YSpeed = bread.XSpeed;
}
This version of scaleSprites
sets the width, height, speed, and initial position of the bread and
the cheese sprites. It makes the bread take up slightly more of the
width of the screen and allows it to move a bit faster than the cheese.
The ScaleSprites method also sets the
initial position of the bread at the middle of the screen and places
the cheese at the top left corner of the display area.
2. Using the Gamepad Thumbsticks to Control Movement
You’ve decided that
the player will control the bread and use it as a bat to hit the cheese.
To make the bread move, you need to add some statements to the Update
method. This turns out to be very easy. The Xbox gamepad has two
thumbsticks that can be used to control games. These generate
floating-point values that you can use to direct the movement of the
bread bat. Figure 1
shows the range of values that the thumbstick produces. If it’s pushed
all the way to the left, it will generate –1.0 for the X value. If it’s
pushed halfway to the left, it will generate –0.5. If the thumbstick is
left in the center, the X and Y values are zero.
You’ve used the GamePadState structure before to read the state of buttons on a gamepad. It also provides a ThumbSticks
property that contains two vectors (one for each thumbstick) that allow
your program to read the current thumbstick values. Version 2.0 of the
Zune (the devices with a Zune pad) map input from the pad onto the left
thumbstick.
To get the amount of
movement of the bread, you simply need to take the values from the left
thumbstick and multiply them by the speed values for your bread sprite.
The farther the thumbstick is moved, the bigger the values and the
faster the bread moves across the screen:
GamePadState gamePad1 = GamePad.GetState(PlayerIndex.One);
// Allows the game to exit
if (gamePad1.Buttons.Back == ButtonState.Pressed)
this.Exit();
// Move the bread
bread.X = bread.X + (bread.XSpeed * gamePad1.ThumbSticks.Left.X);
bread.Y = bread.Y - (bread.YSpeed * gamePad1.ThumbSticks.Left.Y);
bread.SpriteRectangle.X = (int)bread.X;
bread.SpriteRectangle.Y = (int)bread.Y;
This code is placed in the Update
method and updates the position of the bread rectangle according to the
setting of the left thumbstick. Note that the code must subtract the
speed value from the Y coordinate. This is because the Y coordinate goes down the screen, with 0 at the top. If the speed value was added to the Y
coordinate, the bread would go down the screen when the thumbstick is
moved up, making it harder to control. This version of the bread
movement does not restrict the bread to the screen, so it is possible
for the player to move the bread right off the screen.